﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Web.Hosting;
using Bootstrap.Services;
using Nustache.Core;
using Orchard;
using Orchard.Commands;

namespace Bootstrap.CodeGeneration {
    public class BootstrapGenerationCommands : DefaultOrchardCommandHandler {
        private readonly IOrchardServices _services;

        public BootstrapGenerationCommands(IOrchardServices services) {
            _services = services;
        }

        private string RootPath {
            get { return HostingEnvironment.MapPath("~/Themes/Bootstrap/"); }
        }

        private string LessPath {
            get { return RootPath + "\\Styles\\"; }
        }

        private string TemplatePath {
            get { return RootPath + "\\CodeGeneration\\"; }
        }

        [OrchardSwitch]
        public bool Overwrite { get; set; }

        [OrchardSwitch]
        public bool IncludeInProject { get; set; }

        private static Func<Match, string, string> Value {
            get { return (m, x) => m.Groups[x].Value; }
        }

        [OrchardSwitch]
        public string Script { get; set; }

        [CommandName("bootstrap generate variables")]
        [CommandHelp(
            "bootstrap generate variables\n\t" +
            "Generates IBootstrapVariable classes for all variables present in variables.less"
            )]
        public string GenerateVariables() {
            const string pattern = @"@(?<Variable>[^:]*):(?<Value>[^;]*);.*\n|//\s(?<Category>[A-Z|\s]*)\n|//\s(?<Section>[^\n]*)\n//\s\-*\n";


            var reader = new StreamReader(LessPath + "\\variables.less") as TextReader;
            string text = reader.ReadToEnd();

            MatchCollection matches = Regex.Matches(text, pattern);

            var result = new Dictionary<string, Dictionary<string, Dictionary<string, string>>>();
            string cCategory = string.Empty;
            string cSection = string.Empty;
            bool headerFound = false;
            foreach (Match match in matches) {
                string category = Value(match, "Category").Trim();
                string section = Value(match, "Section").Trim();
                string variable = Value(match, "Variable").Trim();
                string val = Value(match, "Value").Trim();


                if (section != string.Empty && cCategory == string.Empty) {
                    //Found global header?
                    if (!headerFound) {
                        headerFound = true;
                        continue;
                    }

                    category = section;
                    section = string.Empty;
                }

                if (category != string.Empty) {
                    cCategory = CultureInfo.InvariantCulture.TextInfo.ToTitleCase(category.ToLower());
                    if (!result.ContainsKey(cCategory)) {
                        result.Add(cCategory, new Dictionary<string, Dictionary<string, string>>());
                    }
                }
                else if (section != string.Empty && section.Any(x => x != '-')) {
                    cSection = CultureInfo.InvariantCulture.TextInfo.ToTitleCase(section.ToLower());
                    if (!result[cCategory].ContainsKey(cSection)) {
                        result[cCategory].Add(cSection, new Dictionary<string, string>());
                    }
                }
                else if (variable != string.Empty) {
                    result[cCategory][cSection].Add(variable, val);
                }
            }
            int cPriority = 1001;

            var catModel = new CategoriesTemplateModel();
            var sectionsModel = new SectionsTemplateModel();
            var vars = new List<VariableTemplateModel>();

            result.Keys.ToList().ForEach(
                x => {
                    string cName = x.Replace(" ", "");
                    catModel.Categories.Add(
                        new CategoryTemplateModel {
                            Name = cName,
                            FullName = x,
                            Priority = (--cPriority).ToString()
                        }
                        );
                    int sPriority = 1001;
                    result[x].Keys.ToList().ForEach(
                        y => {
                            string sName = y.Replace(" ", "").Replace("-", "");
                            sectionsModel.Sections.Add(
                                new SectionTemplateModel {
                                    Name = sName.Replace("&", "And"),
                                    FullName = y,
                                    Priority = (--sPriority).ToString(),
                                    Category = cName
                                }
                                );
                            int vPriority = 1001;
                            result[x][y].Keys.ToList().ForEach(
                                z =>
                                vars.Add(
                                    new VariableTemplateModel {
                                        ClassName = (Char.ToUpper(z[0]) + z.Remove(0, 1)),
                                        Section = sName,
                                        Value = result[x][y][z].Replace("\\", "\\\\").Replace("\"", "\\\""),
                                        VarName = z,
                                        Priority = (--vPriority).ToString(),
                                    }
                                    )
                                );
                        }
                        );
                });

            Render.FileToFile(
                (TemplatePath + "VariableCategories.mustache"),
                catModel,
                RootPath + "\\Variables\\VariableCategories.cs"
                );
            Console.WriteLine("Updated Categories");

            Render.FileToFile(
                (TemplatePath + "Sections.mustache"),
                sectionsModel,
                RootPath + "\\Variables\\VariableSections.cs"
                );
            Console.WriteLine("Updated Sections");

            foreach (VariableTemplateModel v in vars) {
                Render.FileToFile(
                    (TemplatePath + "Variable.mustache"),
                    v,
                    RootPath + "\\Variables\\" + v.ClassName + ".cs"
                    );
                Console.WriteLine("Updated " + v.VarName);
            }


            return "Complete";
        }

        [CommandName("bootstrap build")]
        [CommandHelp(
            "bootstrap build\n\t" +
            "Builds a static copy of Bootstrap in CSS to Styles/bootstrap.css\n" +
            "This will suppress the dynamic build controller"
            )]
        public string StaticBuild() {
            var builder = _services.WorkContext.Resolve<IBootstrapBuildService>();
            builder.BuildToFile();

            return "Build Complete";
        }


        [CommandName("bootstrap generate features")]
        [CommandHelp(
            "bootstrap generate features \n\t" +
            "Generarates IBootstrapFeature classes for all features present in bootstrap.less"
            )]
        public string GenerateFeatures() {
            const string pattern = "\\n//\\s(?<category>[\\w|:|\\s]*)\\n|//(?<comment>[\\w|:|\\s]*)|@import \\\"(?<feature>[^\\\"]*).less\\\";";
            var reader = new StreamReader(LessPath + "\\bootstrap.less.default") as TextReader;
            var text = reader.ReadToEnd();

            MatchCollection matches = Regex.Matches(text, pattern);

            string cCategory = string.Empty;
            var results = new Dictionary<string, Dictionary<string, string>>();

            foreach (Match match in matches) {
                string category = Value(match, "category");
                string feature = Value(match, "feature");
                //var comment = Value(match, "comment"); //we're capturing them solely becuase I'd hope we could gleen useful info from them in the future.

                if (category != string.Empty) {
                    cCategory = category;
                    results[cCategory] = new Dictionary<string, string>();
                }
                else if (feature != string.Empty) {
                    results[cCategory].Add(
                        CultureInfo.InvariantCulture.TextInfo.ToTitleCase(feature),
                        feature
                        );
                }
            }
            int cPriority = 1001;
            var catModel = new CategoriesTemplateModel();
            var featuresModel = new List<FeatureTemplateModel>();
            int position = 0;
            results.Keys.ToList().ForEach(
                x => {
                    string cName = x.Split(new[] {':', '&', ' '}, StringSplitOptions.RemoveEmptyEntries).Aggregate("", (current, str) => current + CultureInfo.InvariantCulture.TextInfo.ToTitleCase(str.Trim()));
                    catModel.Categories.Add(
                        new CategoryTemplateModel {
                            Name = cName,
                            FullName = x,
                            Priority = (--cPriority).ToString(),
                        }
                        );
                    int fPriority = 1001;
                    results[x].Keys.ToList().ForEach(
                        y =>
                        featuresModel.Add(
                            new FeatureTemplateModel {
                                Category = cName,
                                ClassName = y.Replace("-", ""),
                                FeatureName = y.Replace("-", " "),
                                FileName = results[x][y],
                                Priority = (--fPriority).ToString(),
                                Position = (++position).ToString()
                            }
                            )
                        );
                }
                );

            Render.FileToFile(
                (TemplatePath + "FeatureCategories.mustache"),
                catModel,
                RootPath + "\\Features\\FeatureCategories.cs"
                );
            Console.WriteLine("Updated Categories");
            foreach (FeatureTemplateModel feature in featuresModel) {
                feature.Import = feature.FeatureName != "Variables";

                Render.FileToFile(
                    (TemplatePath + "Feature.mustache"),
                    feature,
                    RootPath + "\\Features\\" + feature.ClassName + ".cs"
                    );
                Console.WriteLine("Updated " + feature.FeatureName);
            }

            return "Complete";
        }

        #region Nested type: CategoriesTemplateModel

        public class CategoriesTemplateModel {
            public CategoriesTemplateModel() {
                Categories = new List<CategoryTemplateModel>();
            }

            public List<CategoryTemplateModel> Categories { get; private set; }
        }

        #endregion

        #region Nested type: CategoryTemplateModel

        public class CategoryTemplateModel {
            public string Name { get; set; }
            public string FullName { get; set; }
            public string Priority { get; set; }
        }

        #endregion

        #region Nested type: FeatureTemplateModel

        public class FeatureTemplateModel {
            public string ClassName { get; set; }
            public string FeatureName { get; set; }
            public string Category { get; set; }
            public string FileName { get; set; }
            public bool Required { get; set; }
            public bool Import { get; set; }
            public string Priority { get; set; }
            public string Position { get; set; }
        }

        #endregion

        #region Nested type: SectionTemplateModel

        public class SectionTemplateModel {
            public string Name { get; set; }
            public string FullName { get; set; }
            public string Priority { get; set; }
            public string Category { get; set; }
        }

        #endregion

        #region Nested type: SectionsTemplateModel

        public class SectionsTemplateModel {
            public SectionsTemplateModel() {
                Sections = new List<SectionTemplateModel>();
            }

            public List<SectionTemplateModel> Sections { get; private set; }
        }

        #endregion

        #region Nested type: VariableTemplateModel

        public class VariableTemplateModel {
            public string ClassName { get; set; }
            public string VarName { get; set; }
            public string Section { get; set; }
            public string Value { get; set; }
            public string Priority { get; set; }
        }

        #endregion
    }
}